package com.tuozixuan.jmh.me;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 3, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Warmup(iterations = 3, time = 100, timeUnit = TimeUnit.MILLISECONDS)
public class RepeatJudgeWithHasRepeat {
	
    @State(Scope.Benchmark)
    public static class Params {

    	@Param({"100", "1000", "10000", "100000"})
        private int size;

    	private List<Obj> list;

        public int getSize() {
			return size;
		}

		public void setSize(int size) {
			this.size = size;
		}

		public List<Obj> getList() {
			return list;
		}

		public void setList(List<Obj> list) {
			this.list = list;
		}

        @Setup
        public void setUp() {
        	
        	list = new ArrayList<>();
        	Obj obj = null;
    		for (int i = 0; i < size; i++) {
    			obj = new Obj();
    			if (i == size/2) {
    				obj.setCode("item" + (i-1)); // 重複項
    			} else {
    				obj.setCode("item" + i);
    			}
    			
    			list.add(obj);
    		}
        }
    }
	

	@Benchmark
	public void useStreamDistinct(Params params, Blackhole bh) {
		
		List<Obj> list = params.getList();
		boolean hasRepeat = list.size() != list.stream().map(Obj::getCode).distinct().count();
		bh.consume(hasRepeat);
	}
	
	@Benchmark
	public void useParallelStreamDistinct(Params params, Blackhole bh) {
		
		List<Obj> list = params.getList();
		boolean hasRepeat = list.size() != list.parallelStream().map(Obj::getCode).distinct().count();
		bh.consume(hasRepeat);
	}
	
	@Benchmark
	public void useHashSet(Params params, Blackhole bh) {
		
		List<Obj> list = params.getList();
		Set<String> uniqueCodes = new HashSet<>();
		boolean hasRepeat = false;
		for (Obj obj : list) {
		    if (!uniqueCodes.add(obj.getCode())) {
		    	hasRepeat = true;
		        break;
		    }
		}
		bh.consume(hasRepeat);
	}
	
	public static void main(String[] args) throws RunnerException {
		Options opt = new OptionsBuilder().include(RepeatJudgeWithHasRepeat.class.getSimpleName()).forks(1).build();
		new Runner(opt).run();
	}
	
	static class Obj {
		
		private String code;

		public String getCode() {
			return code;
		}

		public void setCode(String code) {
			this.code = code;
		}
		
	}
}
